package logic

import (
	"cloud-disk/global/db"
	"cloud-disk/model/entity"
	"cloud-disk/model/request"
	"cloud-disk/model/response"
	"cloud-disk/util"
	"errors"
	"fmt"
	"log"
	"time"
)

func CreateShareLogic(userIdentity string, req request.CreateShare) (*response.CreateShare, error) {
	uuid := util.UUID()
	code := util.MixCaptcha(4)
	shares := make([]entity.Share, len(req.Identities))
	now := time.Now()
	for i := 0; i < len(req.Identities); i++ {
		share := entity.Share{
			Identity:               uuid,
			UserIdentity:           userIdentity,
			UserRepositoryIdentity: req.Identities[i],
			ExpiredTime:            req.ExpiredTime,
			Code:                   code,
			CreatedAt:              now,
			UpdatedAt:              now,
		}
		shares[i] = share
	}
	resp := response.CreateShare{
		Identity: uuid,
		Code:     code,
	}
	err := db.DB().Create(&shares).Error
	if err != nil {
		log.Println(err)
		return nil, errors.New("share fail")
	}
	return &resp, nil
}

func AccessShareFileLogic(req request.AccessShareFile) (*[]response.AccessShareFile, error) {
	resp := []response.AccessShareFile{}
	shares := []entity.Share{}
	left := db.DB().Debug().
		Select("user_repository_identity, expired_time").
		Table("share_basic as s").
		Where("s.identity=? and s.code=? and s.status=1", req.Identity, req.Code).Scan(&shares)
	if left.Error != nil {
		log.Println(left.Error)
		return nil, errors.New("code incorrect")
	}

	// 过期处理
	if shares[0].ExpiredTime > 0 && time.Since(shares[0].CreatedAt) > time.Hour*24*time.Duration(shares[0].ExpiredTime) {
		// 修改share的status为0
		err := db.DB().Model(&entity.Share{}).Where("identity=?", req.Identity).Update("status", 0).Error
		if err != nil {
			log.Println(left.Error)
			return nil, errors.New("unknown error")
		}
		return nil, errors.New("this share was overdue")
	}
	// 没过期, 进行如下查询分享文件
	err := db.DB().Debug().
		Select("ur.id, ur.identity, ur.user_identity,ur.parent_id,ur.repository_identity, ur.type,ur.name,rp.size,ur.created_at, ur.updated_at,rp.status,sur.expired_time").
		Table("(?) as sur", left).
		Joins("left join user_repository as ur on ur.identity=sur.user_repository_identity").
		Joins("left join repository_pool as rp on rp.identity=ur.repository_identity").
		Where("(ur.type='file' and rp.status=1) or ur.type='dir'").
		Scan(&resp).Error
	if err != nil {
		log.Println(err)
		return nil, errors.New("unknown error")
	}
	return &resp, nil
}

// 接收一个share主的userIdentity，选中文件的parentId和identity和id
func SaveShareLogic(currUserIdentity string, saveParentId int, req entity.UserRepository) error {
	tx := db.DB().Begin()
	// 创建bfs队列
	var err error
	originQ := []entity.UserRepository{req}
	saveQ := []entity.UserRepository{{
		Identity:           util.UUID(),
		UserIdentity:       currUserIdentity,
		ParentId:           saveParentId,
		RepositoryIdentity: req.Identity,
		Type:               req.Type,
		Name:               req.Name,
	}}

	for len(originQ) > 0 {
		size := len(originQ)
		// 遍历原节点,  然后根据原节点信息
		for k := 0; k < size; k++ {
			// 遍历源目录结构 拿到每一层的节点信息  并将这些节点信息也应用到需要保存的下一层节点中
			originNode := originQ[k]
			infos := []entity.UserRepository{}
			// 获取当前id下的所有节点信息
			err = tx.Select("id, identity,user_identity,parent_id,repository_identity,type,name").Where("user_identity=? and parent_id=?", req.UserIdentity, originNode.Id).
				Model(&entity.UserRepository{}).
				Find(&infos).Error
			if err != nil {
				fmt.Println(err)
				db.DB().Rollback()
				return errors.New("origin repository get failed")
			}
			// 加入队列
			originQ = append(originQ, infos...)

			saveNode := saveQ[k]
			// 需要保存的当前节点
			ur := entity.UserRepository{
				Identity:           util.UUID(),
				UserIdentity:       currUserIdentity,
				ParentId:           saveNode.ParentId,
				RepositoryIdentity: saveNode.RepositoryIdentity,
				Type:               saveNode.Type,
				Name:               saveNode.Name,
			}
			err = tx.Debug().Create(&ur).Error
			if err != nil {
				db.DB().Rollback()
				return errors.New("save error")
			}
			// 根据origin的infos，填充接下来的一些源文件的相关信息，并将perentId配置成刚插入的id
			for i := 0; i < len(infos); i++ {
				saveChildNode := entity.UserRepository{
					ParentId:           ur.Id,
					RepositoryIdentity: infos[i].RepositoryIdentity,
					Type:               infos[i].Type,
					Name:               infos[i].Name,
				}
				saveQ = append(saveQ, saveChildNode)
			}
		}
		saveQ = saveQ[size:]
		originQ = originQ[size:]
	}
	tx.Commit()
	return nil
}
